﻿#include "EditorTextFilePageView.h"

#include "EditorTextFilePageViewModel.h"

#include "Widgets/EditorScrollWidget.h"
#include "Widgets/CanvasWidget.h"
#include "Widgets/Mouse/MouseWidget.h"
#include "Widgets/Keyboard/KeyboardWidget.h"


#include "Font/FontSubsystem.h"

#include "Widgets/Window.h"

#include "Framework/Editor.h"
#include "Theme/EditorTheme.h"

#include "Output/Console.h"

void REditorTextFilePageView::BindViewModel(TSharedObjectPtr<REditorPageViewModel> InViewModel)
{
    ViewModel = InViewModel;
}

void REditorTextFilePageView::CreateWidget(TSharedObjectPtr<RWidget> InParent)
{
    auto Canvas=NewWidget<RCanvasWidget>(InParent);
    Canvas->Anchor.Fill(InParent);
    RCanvasWidgetWeakPtr WeakCanvas=Canvas;
    Canvas->OnPaint.BindLambda([this,WeakCanvas]()
    {
        WeakCanvas->Clear();

        float LineHeight = this->ViewModel->GetLineHeight();
        assert(LineHeight>0);

        ViewModel->UpdateVisibleLineRange(WeakCanvas->PresentSize);
        int64_t MinVisibleLineNumber=ViewModel->GetMinVisibleLine();
        int64_t MaxVisibleLineNumber=ViewModel->GetMaxVisibleLine();

        auto Font=ViewModel->GetFont();
        auto FontSize=ViewModel->GetFontSize();
        //draw line numbers 
        const int64_t LineNumberWidth=ViewModel->GetLineNumberTextWidth();
        for(int64_t i=MinVisibleLineNumber;i<=MaxVisibleLineNumber;++i)
        {   
            //draw line number
            int64_t LineNumber=this->ViewModel->GetLineIndex(i)+1;
            float YOffset=ViewModel->GetContentAreaYOffset();
            float Y=i*LineHeight-YOffset;

            auto FormerLineNumberItem=FormerLineNumberItems.Find(i);
            if(FormerLineNumberItem &&YOffset==FormerYOffset
            )
            {
                WeakCanvas->AddItem(*FormerLineNumberItem);
            }
            else
            {

                auto Item=WeakCanvas->AddTextWithAlignment(
                    SRect(SVec2(0,Y),SVec2(LineNumberWidth,LineHeight))
                    ,CString(LineNumber)
                    ,this->ViewModel->GetLineNumberTextColor(i)
                    ,FontSize
                    ,ETextHorizontalAlignment::Right
                    ,ETextVerticalAlignment::Center
                    ,Font
                );

                FormerLineNumberItems.Add(i,Item);
            }

            const float DebugPointRadius=5;
            const float DebugLeftOffset=5;
            switch(ViewModel->GetDebugPointType(i))
            {
                case REditorTextFilePageViewModel::EDebugPointType::None:
                    break;
                case REditorTextFilePageViewModel::EDebugPointType::Normal:
                    WeakCanvas->AddCircle(
                        SVec2(DebugLeftOffset+DebugPointRadius,Y+LineHeight*0.5f)
                        ,DebugPointRadius
                        ,ViewModel->GetBreakpointColor()
                    );
                    break;
                case REditorTextFilePageViewModel::EDebugPointType::Disabled:
                    WeakCanvas->AddCircle(
                        SVec2(DebugLeftOffset+DebugPointRadius,Y+LineHeight*0.5f)
                        ,DebugPointRadius
                        ,ViewModel->GetDisabledDebugPointColor()
                    );
                    break;
                case REditorTextFilePageViewModel::EDebugPointType::Adding:
                    WeakCanvas->AddCircle(
                        SVec2(DebugLeftOffset+DebugPointRadius,Y+LineHeight*0.5f)
                        ,DebugPointRadius
                        ,ViewModel->GetAddingBreakpointColor()
                    );
                    break;
                default:
                    break;
            }
        }
        FormerYOffset=ViewModel->GetContentAreaYOffset();

        //这个函数用于高亮一部分代码
        auto HighlightTextRangeFunc=
            [this,WeakCanvas,LineHeight,Font,FontSize]
            (int32_t StartLineIndex
            , int32_t StartColumnIndex
            , int32_t EndLineIndex
            , int32_t EndColumnIndex
            , const SColor &InColor)
        {
            //高亮一部分代码
            if(StartLineIndex==EndLineIndex)
            {
                //选中区域只有一行
                CString LineContent=ViewModel->GetLineContent(StartLineIndex);

                float StartX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                    LineContent.SubString(0,StartColumnIndex)
                    ,Font
                    ,FontSize
                );

                float EndX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                    LineContent.SubString(0,EndColumnIndex)
                    ,Font
                    ,FontSize
                );

                float Y=StartLineIndex*LineHeight-ViewModel->GetContentAreaYOffset();
                float Height=LineHeight;

                WeakCanvas->AddRect(
                    SRect(SVec2(StartX,Y),SVec2(EndX-StartX,Height))
                    ,InColor
                );

            }
            else
            {
                //高亮区域有多行
                //绘制第一行

                //换行符的宽度
                float NewLineWidth=  FontSize*0.5f;
                {
                    CString StartLineContent=ViewModel->GetLineContent(StartLineIndex);
                    float StartX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                        StartLineContent.SubString(0,StartColumnIndex)
                        ,Font
                        ,FontSize
                    );

                    float EndX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                        StartLineContent
                        ,Font
                        ,FontSize
                    );
                    EndX+=NewLineWidth;

                    float Y=StartLineIndex*LineHeight-ViewModel->GetContentAreaYOffset();
                    float Height=LineHeight;

                    WeakCanvas->AddRect(
                        SRect(SVec2(StartX,Y),SVec2(EndX-StartX,Height))
                        ,InColor
                    );
                }

                //绘制中间行

                if(EndLineIndex-StartLineIndex>1)
                {
                    for(int64_t i=StartLineIndex+1;i<EndLineIndex;++i)
                    {
                        float Y=i*LineHeight-ViewModel->GetContentAreaYOffset();
                        float Height=LineHeight;

                        float StartX=ViewModel->CodeAreaLeftOffset;
                        float EndX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                            ViewModel->GetLineContent(i)
                            ,Font
                            ,FontSize
                        );

                        EndX+=NewLineWidth;

                        WeakCanvas->AddRect(
                            SRect(SVec2(StartX,Y),SVec2(EndX-StartX,Height))
                            ,InColor
                        );
                    }
                }

                //绘制最后一行
                {
                    CString EndLineContent=ViewModel->GetLineContent(EndLineIndex);
                    float StartX=ViewModel->CodeAreaLeftOffset;
                    float EndX=ViewModel->CodeAreaLeftOffset+RFontApplicationSubsystem::GetInstance()->GetSingleLineOccupiedWidth(
                        EndLineContent.SubString(0,EndColumnIndex)
                        ,Font
                        ,FontSize
                    );

                    float Y=EndLineIndex*LineHeight-ViewModel->GetContentAreaYOffset();
                    float Height=LineHeight;

                    WeakCanvas->AddRect(
                        SRect(SVec2(StartX,Y),SVec2(EndX-StartX,Height))
                        ,InColor
                    );
                }
            }

        };

        for(auto& HighLightRange : ViewModel->GetHighlightRanges())
        {
            HighlightTextRangeFunc(
                HighLightRange.StartLineIndex
                ,HighLightRange.StartColumnIndex
                ,HighLightRange.EndLineIndex
                ,HighLightRange.EndColumnIndex
                ,HighLightRange.Color
            );
        }
        
        //draw line content
        int64_t StartCharIndex=-1;
        int64_t EndCharIndex=-1;
        auto VisibleText=ViewModel-> GetTextOfRange(
            MinVisibleLineNumber
            ,MaxVisibleLineNumber
            ,StartCharIndex
            ,EndCharIndex
        );


        if(this->ViewModel->HasTextRanderer())
        {
            SVec2 Position= SVec2(ViewModel->CodeAreaLeftOffset
                ,MinVisibleLineNumber*LineHeight-ViewModel->GetContentAreaYOffset());
            
            TVector<SColorfulTextColor> TextColors=ViewModel->GetTextColorsOfRange(StartCharIndex,EndCharIndex);

            if(VisibleText!=FormerText
                || FormerTextColors!=TextColors
                || FormerFont!=Font
                || FormerFontSize!=FontSize
                || FormerPosition!=Position)
            {
                FormerItems=WeakCanvas->AddColorfulText(
                    Position
                    ,VisibleText
                    ,TextColors
                    ,FontSize
                    ,Font
                );

                FormerText=VisibleText;
                FormerTextColors=ViewModel->GetTextColorsOfRange(StartCharIndex,EndCharIndex);
                FormerFont=Font;
                FormerFontSize=FontSize;
                FormerPosition=Position;
            }
            else
            {
                if(FormerItems) //如果文本为空，则这个Item也为空
                {
                    WeakCanvas->AddItem(FormerItems);
                }
            }

            // WeakCanvas->AddColorfulText(
            //     SVec2(ViewModel->CodeAreaLeftOffset,
            //     MinVisibleLineNumber*LineHeight-ViewModel->GetContentAreaYOffset()
            //     )
            //     ,VisibleText
            //     ,ViewModel->GetTextColorsOfRange(StartCharIndex,EndCharIndex)
            //     ,FontSize
            //     ,Font
            // );
        }
        else
        {
            WeakCanvas->AddTextWithAlignment(
                SRect(SVec2(ViewModel->CodeAreaLeftOffset
                ,MinVisibleLineNumber*LineHeight-ViewModel->GetContentAreaYOffset()
                ),SVec2(9999,(MaxVisibleLineNumber-MinVisibleLineNumber)*LineHeight ))
                ,VisibleText
                ,SColor(1,1,1,1)
                ,FontSize
                ,ETextHorizontalAlignment::Left
                ,ETextVerticalAlignment::Top
                ,Font
            );
        }
           
        //draw cursor
        if(this->ViewModel->ShouldShowCursor())
        {
            int64_t CursorLineIndex=this->ViewModel->GetCursorLineIndex();
            int64_t CursorColumnIndex=this->ViewModel->GetCursorColumnIndex();
            if(CursorLineIndex>=MinVisibleLineNumber && CursorLineIndex<=MaxVisibleLineNumber)
            {

                float FormerWidth=0;
                CString LineContent=this->ViewModel->GetLineContent(CursorLineIndex);
                if(!LineContent.IsEmpty())
                {
                    CString FormerContent=
                        LineContent.GetLength()> CursorColumnIndex ?
                        LineContent.SubString(0,CursorColumnIndex)
                        : LineContent
                        ;
                    FormerWidth=RFontApplicationSubsystem::GetInstance()->
                    GetSingleLineOccupiedWidth(
                        FormerContent
                        ,Font
                        ,FontSize);
                }
                const float CursorWidth=2;
                float X=this->ViewModel->CodeAreaLeftOffset+FormerWidth - CursorWidth/2;
                float Y=CursorLineIndex*LineHeight-ViewModel->GetContentAreaYOffset();
                
                WeakCanvas->AddLine(
                    SVec2(X,Y)
                    ,SVec2(X,Y+LineHeight)
                    ,2
                    ,SColor(0.7,0.7,0.7,1)
                );
            }
        }  

        //draw a TextContentThumbnail at right of content
        if(this->ViewModel->HasTextRanderer())
        {
            //paint a split line at left of thumbnail
            WeakCanvas->AddLine(
                SVec2(WeakCanvas->PresentSize.X-ViewModel->TextContentThumbnailWidth-ViewModel->VerticalScrollBarWidth,0)
                ,SVec2(WeakCanvas->PresentSize.X-ViewModel->TextContentThumbnailWidth-ViewModel->VerticalScrollBarWidth,WeakCanvas->PresentSize.Y)
                ,2
                ,SColor(0.2,0.2,0.2,1)
            );

            //paint thumbnail
            CString WholeText=ViewModel->GetWholeText();
            SVec2 ThumbnailPosition=SVec2(WeakCanvas->PresentSize.X-ViewModel->TextContentThumbnailWidth-ViewModel->VerticalScrollBarWidth,0);
            TVector<SColorfulTextColor> TextColors=ViewModel->GetTextColors();
            
            if(WholeText!=FormerWholeText 
            || TextColors!=FormerThumbnailTextColors 
            || Font!=FormerThumbnailFont 
            || ThumbnailPosition!=FormerThumbnailPosition)
            {
                FormerThumbnailItems=WeakCanvas->AddColorfulText(
                    ThumbnailPosition
                    ,WholeText
                    ,TextColors
                    ,ViewModel->ThumbnailFontSize
                    ,Font
                );

                FormerWholeText=WholeText;
                FormerThumbnailTextColors=TextColors;
                FormerThumbnailFont=Font;
                FormerThumbnailPosition=ThumbnailPosition;
                
            }
            else
            {
                if(FormerThumbnailItems)//如果文本为空，则这个Item也为空
                {
                    WeakCanvas->AddItem(FormerThumbnailItems);
                }
            }
        }

        //draw vertical scroll bar background, to cover right side of the text content thumbnail
        WeakCanvas->AddRect(
            SRect(SVec2(WeakCanvas->PresentSize.X-ViewModel->VerticalScrollBarWidth,0),SVec2(ViewModel->VerticalScrollBarWidth,WeakCanvas->PresentSize.Y))
            ,REditor::Get()->CurrentTheme->ForegroundColor
        );

        //draw split line at right of the text content thumbnail
        WeakCanvas->AddLine(
            SVec2(WeakCanvas->PresentSize.X-ViewModel->VerticalScrollBarWidth,0)
            ,SVec2(WeakCanvas->PresentSize.X-ViewModel->VerticalScrollBarWidth,WeakCanvas->PresentSize.Y)
            ,1
            ,REditor::Get()->CurrentTheme->ScrollBarSpliterColor
        );

        auto VerticalScrollBarPositionAndHeight=ViewModel->GetVerticalScrollbarPositionAndHeight(WeakCanvas->PresentSize.Y);
        WeakCanvas->AddRect(
            SRect(SVec2(WeakCanvas->PresentSize.X-ViewModel->VerticalScrollBarWidth,VerticalScrollBarPositionAndHeight.Position),SVec2(ViewModel->VerticalScrollBarWidth,VerticalScrollBarPositionAndHeight.Height))
            ,REditor::Get()->CurrentTheme->ScrollBarColor
        );
    }); 

    //line number mouse 
    auto LineNumberMouse=NewWidget<RMouseWidget>(InParent);
    LineNumberMouse->Anchor.TopReference=InParent;
    LineNumberMouse->Anchor.BottomReference=InParent;
    LineNumberMouse->Anchor.LeftReference=InParent;
    LineNumberMouse->Width= ViewModel->GetLineNumberTextWidth();
    LineNumberMouse->CursorShape=EMouseCursorType::Hand;
    RMouseWidgetWeakPtr WeakLineNumberMouse=LineNumberMouse;
    LineNumberMouse->OnMoved.BindLambda([this,WeakLineNumberMouse](const SMouseButtonEvent& InEvent)
    {
        ViewModel->HovorLineNumber(WeakLineNumberMouse->GetMousePositionInWidget());
        return true;
    });
    LineNumberMouse->OnClicked.BindLambda([this,WeakLineNumberMouse](const SMouseButtonEvent& InEvent)
    {
        ViewModel->AddOrRemoveBreakpoint(WeakLineNumberMouse->GetMousePositionInWidget());
        return true;
    });
    LineNumberMouse->OnExited.BindLambda([this]()
    {
        ViewModel->ClearHoverLineNumber();
    });



    //File content area mouse
    auto ContentMouse=NewWidget<RMouseWidget>(InParent);
    ContentMouse->CursorShape=EMouseCursorType::IBeam;
    ContentMouse->Anchor.TopReference=InParent;
    ContentMouse->Anchor.BottomReference=InParent;
    ContentMouse->Anchor.LeftReference=InParent;
    ContentMouse->Anchor.LeftOffset= ViewModel->CodeAreaLeftOffset;
    ContentMouse->Anchor.RightReference=InParent;
    
    //利用拖拽实现文本选择
    ContentMouse->Drag->bIsEnabled=true;

    float RightOffset= ViewModel->VerticalScrollBarWidth;
    if(ViewModel->HasTextRanderer())
    {
        RightOffset+=ViewModel->TextContentThumbnailWidth;
    }
    ContentMouse->Anchor.RightOffset= RightOffset;

    //keyboard event
    auto Keyboard=NewWidget<RKeyboardWidget>(ContentMouse);
    Keyboard->Anchor.Fill(ContentMouse);
    RKeyboardWidgetWeakPtr WeakKeyboard=Keyboard;

    RMouseWidgetWeakPtr WeakContentMouse=ContentMouse;

    auto UpdateCursorPosition=[this,WeakContentMouse]()->SDocumentPosition
    {
        SVec2 MouseRelativePosition= WeakContentMouse->GetMousePositionInWidget();
        
        //在选择文本时可能将光标拽出控件之外，暂时不考虑到外面的情况
        if(MouseRelativePosition.X<0)
        {
            MouseRelativePosition.X=0;
        }
        if(MouseRelativePosition.Y<0)
        {
            MouseRelativePosition.Y=0;
        }

        auto DocumentPosition=ViewModel->CalculateCursorPosition(MouseRelativePosition);
        ViewModel->SetCursorLineIndex(DocumentPosition.LineIndex);
        ViewModel->SetCursorColumnIndex(DocumentPosition.ColumnIndex);
        return DocumentPosition;
    };

    ContentMouse->OnPressed.BindLambda([this,WeakKeyboard,UpdateCursorPosition](const SMouseButtonEvent& InArgs)
    {
        WeakKeyboard->Focus();

        auto CurrentPosition=UpdateCursorPosition();

        //取消选择
        this->ViewModel->SelectionStart=CurrentPosition;
        this->ViewModel->SelectionEnd=CurrentPosition;

        return true;
    });

    ContentMouse->Drag->OnDragStarted.Bind([this,UpdateCursorPosition]()
    {
        //刷新光标位置
        auto CurrentPosition=UpdateCursorPosition();

        //开始选择
        this->ViewModel->SelectionStart=CurrentPosition;
        this->ViewModel->SelectionEnd=CurrentPosition;
    });

    ContentMouse->Drag->OnDragMoved.Bind([this,UpdateCursorPosition](const SVec2 &)
    {
        //刷新光标位置
        auto CurrentPosition=UpdateCursorPosition();
        this->ViewModel->SelectionEnd=CurrentPosition;
        
    });

    Keyboard->OnKeyboardFocused.Bind([this,WeakKeyboard]()
    {
        ViewModel->StartEdit(WeakKeyboard);
    });

    Keyboard->OnKeyboardUnfocused.Bind([this]()
    {
        ViewModel->EndEdit();
    });

    Keyboard->OnTextInput.Bind([this](const STextInputEvent& InEvent)
    {
        ViewModel->OnTextInput(InEvent);
        return true;
    });
    Keyboard->OnKeyboardInput.Bind([this](const SKeyboardEvent& InEvent)
    {
        ViewModel->OnKeyboardEvent(InEvent);
        return true;
    });

    auto VerticalScrollBarMouse=NewWidget<RMouseWidget>(InParent);
    VerticalScrollBarMouse->Anchor.TopReference=InParent;
    VerticalScrollBarMouse->Anchor.BottomReference=InParent;
    VerticalScrollBarMouse->Anchor.RightReference=InParent;
    VerticalScrollBarMouse->Width=ViewModel->VerticalScrollBarWidth;
    VerticalScrollBarMouse->Drag->bIsEnabled=true;
    RMouseWidgetWeakPtr WeakVerticalScrollBarMouse=VerticalScrollBarMouse;
    VerticalScrollBarMouse->Drag->OnDragStarted.Bind([this,WeakCanvas,WeakVerticalScrollBarMouse]()
    {
        auto MousePosition=WeakVerticalScrollBarMouse->GetMousePositionInWidget();
        ViewModel->UpdateVerticalScrollbarPercent(WeakCanvas->PresentSize.Y,MousePosition.Y);
    
        WeakVerticalScrollBarMouse->GetWindow()->StartCaptureMouse();
    });

    VerticalScrollBarMouse->Drag->OnDragMoved.Bind([this,WeakCanvas,WeakVerticalScrollBarMouse](const SVec2 & )
    {
        auto MousePosition=WeakVerticalScrollBarMouse->GetMousePositionInWidget();
        ViewModel->UpdateVerticalScrollbarPercent(WeakCanvas->PresentSize.Y,MousePosition.Y);
    });

    VerticalScrollBarMouse->Drag->OnDragEnded.Bind([this,WeakCanvas,WeakVerticalScrollBarMouse]()
    {
        auto MousePosition=WeakVerticalScrollBarMouse->GetMousePositionInWidget();
        ViewModel->UpdateVerticalScrollbarPercent(WeakCanvas->PresentSize.Y,MousePosition.Y);

        WeakVerticalScrollBarMouse->GetWindow()->StopCaptureMouse();
    });

    VerticalScrollBarMouse->OnPressed.BindLambda([this,WeakCanvas,WeakVerticalScrollBarMouse](const SMouseButtonEvent& InArgs)
    {
        auto MousePosition=WeakVerticalScrollBarMouse->GetMousePositionInWidget();
        ViewModel->UpdateVerticalScrollbarPercent(WeakCanvas->PresentSize.Y,MousePosition.Y);
        return true;
    });

}
